<!doctype html>
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
  <script src="../../../web-component-tester/browser.js"></script>
  <link rel="import" href="../../polymer.html">
  <style>
  .variable-override {
    border-top-width: 10px;
  }
</style>
</head>
<body>

  <simple-element></simple-element>
  <simple-element></simple-element>

  <x-scope></x-scope>

  <dom-module id="x-grand-child-scope">
    <template>
      <style>
        :host {
          display: block;
          padding: 8px;
        }

        #scope {
          border: var(--scope-var);
        }

        #child {
          border: var(--child-scope-var);
        }

        #me {
          border: var(--grand-child-scope-var);
        }

      </style>
      <div id="me">x-grand-child-scope</div>
      <div id="scope">From x-scope</div>
      <div id="child">From x-child-scope</div>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-grand-child-scope'});
    });
    </script>
  </dom-module>

  <dom-module id="x-host-property">
    <template>
      <style>
        :host {
          display: block;
          padding: 8px;
          border: var(--scope-var);
        }

        :host(.foo) {
          border: var(--scope-var-foo);
        }
      </style>
      Host property
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-host-property'});
    });
    </script>
  </dom-module>

  <dom-module id="x-host-host-property">
    <template>
      <x-host-property id="hosted"></x-host-property>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-host-host-property'});
    });
    </script>
  </dom-module>

  <dom-module id="x-produce-use-property">
    <template>
      <style>
        :host {
          --scope-var: 6px solid steelblue;
          --scope-var-foo: 8px solid steelblue;
        }
      </style>
      <x-host-host-property id="hosted"></x-host-host-property>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-produce-use-property'});
    });
    </script>
  </dom-module>

  <dom-module id="x-child-scope">
    <template>
      <style>
        :host {
          display: block;
          padding: 8px;
        }

        :root {
          --gc4-scope: 5px solid green;
        }

        #me {
          border: var(--child-scope-var);
        }

        #gc2 {
          --grand-child-scope-var: 4px solid seagreen;
        }

        #gc4 {
          --grand-child-scope-var:
          var(--gc4-scope);
        }
      </style>
      <div id="me">x-child-scope</div>
      <x-grand-child-scope id="gc1"></x-grand-child-scope>
      <x-grand-child-scope id="gc2"></x-grand-child-scope>
      <x-grand-child-scope id="gc3"></x-grand-child-scope>
      <x-grand-child-scope id="gc4"></x-grand-child-scope>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-child-scope'});
    });
    </script>
  </dom-module>

  <dom-module id="x-overrides">
    <template>
      <style>
        :host {
          border: 1px dashed gray;
          margin: 8px;
          padding: 8px;
          display: block;
          --grand-child-scope-var: var(--rename);
        }
      </style>
      overrides:
      <x-grand-child-scope id="gc1"></x-grand-child-scope>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-overrides'});
    });
    </script>
  </dom-module>

  <dom-module id="x-overrides2">
    <template>
      <style>
        :host {
          border: 1px dashed gray;
          margin: 8px;
          padding: 8px;
          display: block;
        }

        :root {
          --grand-child-scope-var: var(--rename);
        }

      </style>
      overrides:
      <x-grand-child-scope id="gc1"></x-grand-child-scope>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-overrides2'});
    });
    </script>
  </dom-module>

  <dom-module id="x-late">
    <template>
      <style>
        :host {
          border: var(--late);
          display: block;
        }
      </style>
      late
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-late'});
    });
    </script>
  </dom-module>

  <dom-module id="x-overrides3">
    <template>
      <style>
        :host {
          border: 1px dashed gray;
          margin: 8px;
          padding: 8px;
          display: block;
        }

        :root {
          --fillin: 16px;
        }
      </style>
      overrides:
      <x-late id="late"></x-late>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-overrides3'});
    });
    </script>
  </dom-module>

  <dom-module id="x-has-def">
    <template>
      <style>
        :host {
          border: var(--border, var(--defaultBorder));
          margin: 8px;
          padding: 8px;
          display: block;
        }
      </style>
      Element with default variable.
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({is: 'x-has-def'});
    });
    </script>
  </dom-module>

  <dom-module id="x-has-if">
    <template>
      <style>
        .iffy {
          border: var(--scope-var);
        }
      </style>
      <template is="dom-if" if="{{gogo}}">
        <div class="iffy">iffy</div>
      </template>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-has-if',
        properties: {
          gogo: {value: true}
        }
      });
    });
    </script>
  </dom-module>

  <dom-module id="x-button">
    <template>
      <style>
        :host {
          display: block;
          border: var(--button-border);
        }
      </style>
      Button!
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-button',
        extends: 'button'
      });
    });
    </script>
  </dom-module>

  <dom-module id="x-dynamic">
    <template>
      <style>
        :host {
          display: block;
          margin: 20px;
          border: var(--dynamic);
        }
      </style>
      Dynamic
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-dynamic'
      });
    });
    </script>
  </dom-module>

  <dom-module id="x-keyframes">
    <template>
      <style>
        :host {
          display: block;
          position: relative;
          border: 10px solid blue;
          left: 0px;
          /* Prefix required by Safari <= 8 */
          -webkit-animation-duration: 0.3s;
          animation-duration: 0.3s;
          -webkit-animation-fill-mode: forwards;
          animation-fill-mode: forwards;
        }

        :host([animated]) {
          /* Prefix required by Safari <= 8 */
          -webkit-animation-name: x-keyframes-animation;
          animation-name: x-keyframes-animation;
        }

        /* Prefix required by Safari <= 8 */
        @-webkit-keyframes x-keyframes-animation {
          0% {
            left: var(--a);
          }

          100% {
            left: var(--b);
          }
        }
        @keyframes x-keyframes-animation {
          0% {
            left: var(--a);
          }

          100% {
            left: var(--b);
          }
        }
      </style>
      x-keyframes
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-keyframes',
        properties: {
          animated: {
            type: Boolean,
            value: false,
            reflectToAttribute: true
          }
        }
      });
    });
    </script>
  </dom-module>

  <dom-module id="x-scope">
    <template>
      <style>
        :host {
          x--invalid: 15px solid gray;
          display: block;
          padding: 8px;
          --scope-var: 1px solid black;
          --fallback: 7px solid orange;
          --default1: var(--undefined, 6px solid yellow);
          --default2: var(--undefined, var(--fallback));
          --default3: var(--undefined, rgb(128, 200, 250));
          --defaultBorder: 22px solid green;
          --a: 10px;
          --b: 5px;
          --primary-color: rgb(128, 128, 128);
          --late: var(--fillin);

          --button-border: 16px solid tomato;
          --after: 17px solid brown;

          --end-term: 19px solid blue}

        :root{--ws-term: 18px solid orange}

        #me {
          border: var(--scope-var);
        }

        x-child-scope {
          --child-scope-var: 2px solid orange;
          --grand-child-scope-var: 3px solid steelblue;
        }

        x-child-scope.special {
          --child-scope-var: 12px solid orange;
        }

        #applyDefault1 {
          border: var(--undefined, 6px solid yellow);
        }

        #applyDefault2 {
          border: var(--undefined, var(--fallback));
        }

        #default1 {
          border: var(--default1);
        }

        #default2 {
          border: var(--default2);
        }

        #default3 {
          padding: 8px;
          background-color: var(--default3);
        }

        #defaultElement2 {
          --defaultBorder: 23px solid goldenrod;
        }

        #overrides1a, #overrides1b, #overrides2 {
          --rename: 8px solid navy;
        }

        #overrides1b, #overrides2 {
          --grand-child-scope-var: 9px solid orange;
        }

        #overridesConcrete {
          border: var(--scope-var);
          border: 4px solid steelblue;
        }

        #calc {
          border: solid red;
          border-width: calc(var(--a) + var(--b));
        }

        #shadow {
          box-shadow: 10px 10px 10px var(--primary-color);
          -webkit-box-shadow: 10px 10px 10px var(--primary-color);
        }

        x-host-property {
          border: 10px solid purple;
        }

        #invalid {
          border: var(--invalid);
        }

        #after::after {
          content: 'after';
          border: var(--after);
        }

        #wsTerm {
          border: var(--ws-term)
        }

        x-keyframes:nth-of-type(2) {
          --b: -5px;
        }

        #endTerm {border: var(--end-term)}

        #parenthesis {
          background-image: var(--foo-background-image, url(http://placehold.it/400x300));
        }

        #nestedFallback {
          border: var(--undefined, var(--undefined, 20px solid blue));
        }

        #doubleNestedFallback {
          border: var(--undefined, var(--undefined, var(--undefined, 20px solid red)));
        }

        #cornerFallback {
          background-image: var(--undefined, var(--undefined, var(--undefined, url(http://placehold.it/400x300))));
        }
        #badFallback {
          border: var(--undefined, --default1);
        }
        #funkyFallback {
          color: var(--undefined, var(--undefined, --primary-color));
        }
      </style>
      <div id="me">x-scope</div>
      <x-keyframes id="keyframes"></x-keyframes>
      <x-keyframes id="keyframes2"></x-keyframes>
      <x-child-scope id="child"></x-child-scope>
      <x-child-scope id="child2"></x-child-scope>
      <x-overrides id="overrides1a"></x-overrides>
      <x-overrides id="overrides1b"></x-overrides>
      <x-overrides2 id="overrides2"></x-overrides2>
      <x-overrides3 id="overrides3"></x-overrides3>
      <div id="overridesConcrete">override concrete</div>
      <button id="button" is="x-button"></button>
      <div id="default1">default</div>
      <div id="default2">var default</div>
      <div id="default3">tricky property rgb(...) default</div>
      <x-has-def id="defaultElement1"></x-has-def>
      <x-has-def id="defaultElement2"></x-has-def>
      <div id="applyDefault1">default</div>
      <div id="applyDefault2">var default</div>
      <div id="calc">Calc</div>
      <div id="shadow">Shadow</div>
      <div id="invalid">invalid</div>
      <x-host-property id="hostProp"></x-host-property>
      <x-has-if id="iffy"></x-has-if>
      <div id="after"></div>
      <x-dynamic id="dynamic"></x-dynamic>
      <div id="wsTerm">new line var</div>
      <div id="endTerm">end var</div>
      <div id="parenthesis">parenthesis</div>
      <div id="nestedFallback"></div>
      <div id="doubleNestedFallback"></div>
      <div id="cornerFallback"></div>
      <div id="badFallback"></div>
      <div id="funkyFallback"></div>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-scope'
      });
    });
    </script>
  </dom-module>

  <dom-module id="x-inside">
    <template>
      <style>
        :host {
          display: inline-block;
          border: var(--border) solid orange;
          height: 10px;
          width: 10px;
          background-color: tomato;
        }
      </style>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-inside'
      });
    });
    </script>
  </dom-module>

  <dom-module id="simple-element">
    <template>
      <style>
        :host {
          display: block;
        }

        x-inside {
          color: var(--dne);
          --border: 10px;
        }
      </style>
      <x-inside id="inner"></x-inside>
    </template>
    <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'simple-element'
      });
    });
    </script>
  </dom-module>


<dom-module id="x-variable-override">
  <template>
    <style>
      :host {
        --b: 2px solid black;
        display: block;
        border: var(--b);
      }
    </style>
  </template>
  <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-variable-override'
      });
    });
  </script>
</dom-module>

<dom-module id="x-variable-consumer">
  <template>
    <style>
      :host(.foo) {
        border: var(--consumer);
      }
    </style>
    test
  </template>
  <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-variable-consumer'
      });
    });
  </script>
</dom-module>

<dom-module id="x-host-variable-consumer">
  <template>
    <style>
      :host {
        --consumer: 2px solid orange;
      }

      .foo {
        display: block;
        /* should override */
        border: 10px solid black;
      }
    </style>
    <x-variable-consumer id="consumer" class="foo"></x-variable-consumer>
  </template>
  <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-host-variable-consumer'
      });
    });
  </script>
</dom-module>

<dom-module id="x-var-produce-via-consume">
  <template>
    <style>
      :host {
        display: block;
        border: 10px solid orange;
        --foo: var(--bar);
      }
    </style>
        </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-var-produce-via-consume'
    });
  });
  </script>
</dom-module>

<dom-module id="x-non-media-matching">
  <template>
    <style>
      @media (min-width: 100000px) {
        #inside {
          --consumer: 10px solid red;
        }
      }
    </style>
    <x-variable-consumer id="inside" class="foo"></x-variable-consumer>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-non-media-matching'
    });
  });
  </script>
</dom-module>

<dom-module id="x-non-media-matching-host">
  <template>
    <style>
      @media (min-width: 100000px) {
        :host {
          --border: 10px solid red;
        }
      }

      :host {
        display: block;
        border: var(--border);
      }
    </style>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-non-media-matching-host'
    });
  });
  </script>
</dom-module>

<dom-module id="x-super-complex">
  <template>
    <style>
    :host {
      --color1: 102;
      --color2: 205;
      --color3: 170;
      display: block;
      color: var(--undefined, rgb(var(--color1), var(--color2), var(--color3)));
    }
    </style>
    <div>Should be mediumaquamarine</div>
  </template>
  <script>
    HTMLImports.whenReady(function() {
      Polymer({
        is: 'x-super-complex'
      });
    });
  </script>
</dom-module>

<dom-module id="x-update-styles">
  <template>
    <style>
      :host {
        --border: 10px solid red;
      }

      :host([active]) {
        --consumer: 6px solid orange;
      }

      :host {
        display: block;
        border: var(--border);
      }
    </style>
    <x-variable-consumer id="child" class="foo"></x-variable-consumer>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-update-styles',
      properties: {
        active: {
          reflectToAttribute: true,
          observer: '_activeChanged'
        }
      },

      _activeChanged: function() {
        this.updateStyles();
      }
    });
  });
  </script>
</dom-module>

<dom-module id="x-inherit">
  <template>
    <style>
    :host {
      --border: inherit;
      border: var(--border, 10px solid black);
    }
    </style>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-inherit'
    })
  });
  </script>
</dom-module>

<dom-module id="x-inherit-parent">
  <template>
    <style>
    #child {
      --border: 5px solid blue;
    }
    </style>
    <x-inherit id="child"></x-inherit>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-inherit-parent'
    })
  });
  </script>
</dom-module>

<dom-module id="x-initial">
  <template>
    <style>
    :host {
      --border: initial;
      border: var(--border, 10px solid black);
    }
    </style>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({
      is: 'x-initial'
    })
  });
  </script>
</dom-module>

<dom-module id="root-styles">
  <template>
    <style>
    :root {
      --root-border: 2px solid black;
    }
    :host > * {
      --host-star-border: 10px solid orange;
    }
    #root {
      border: var(--root-border);
    }
    #host {
      border: var(--host-star-border);
    }
    </style>
    <div id="root"></div>
    <div id="host"></div>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({is: 'root-styles'});
  });
  </script>
</dom-module>

<dom-module id="prop-inner">
  <script>
  HTMLImports.whenReady(function() {
    Polymer({is: 'prop-inner'});
  });
  </script>
</dom-module>

<dom-module id="prop-outer">
  <template>
    <style>
    :host {
      --foo: orange;
    }
    </style>
    <prop-inner id="inner"></prop-inner>
  </template>
  <script>
  HTMLImports.whenReady(function() {
    Polymer({is: 'prop-outer'});
  });
  </script>
</dom-module>

<script>
  suite('scoped-styling-var', function() {

  function assertComputed(element, value, pseudo, name) {
    name = name || 'border-top-width';
    var computed = element.getComputedStyleValue && !pseudo ?
      element.getComputedStyleValue(name) :
      getComputedStyle(element, pseudo)[name];
    assert.equal(computed, value, 'computed style incorrect');
  }

  function assertStylePropertyValue(element, name, includeValue) {
    var value = element.getComputedStyleValue(name);
    assert.include(value, includeValue);
  }

  var styled = document.querySelector('x-scope');

  test('variables in @keyframes', function(done) {
    var xKeyframes = styled.$.keyframes;
    var onAnimationEnd = function() {
      assertStylePropertyValue(xKeyframes, 'left', '5px');

      xKeyframes.removeEventListener('animationend', onAnimationEnd);
      xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
      xKeyframes.animated = false;
      done();
    };

    assertStylePropertyValue(xKeyframes, '--a', '10px');
    assertStylePropertyValue(xKeyframes, '--b', '5px');

    xKeyframes.addEventListener('animationend', onAnimationEnd);
    xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);

    xKeyframes.animated = true;
  });

  test('instances of scoped @keyframes', function(done) {
    var xKeyframes = styled.$.keyframes2;
    var onAnimationEnd = function() {
      assertStylePropertyValue(xKeyframes, 'left', '5px');

      xKeyframes.removeEventListener('animationend', onAnimationEnd);
      xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
      xKeyframes.animated = false;
      done();
    };

    assertStylePropertyValue(xKeyframes, '--a', '10px');
    assertStylePropertyValue(xKeyframes, '--b', '-5px');

    xKeyframes.addEventListener('animationend', onAnimationEnd);
    xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);

    xKeyframes.animated = true;
  });

  test('mutiple elements in document', function() {
    var e$ = document.querySelectorAll('simple-element');
    assertComputed(e$[0].$.inner, '10px');
    assertComputed(e$[1].$.inner, '10px');
  });

  test('simple variables calculated correctly between scopes', function() {
     assertStylePropertyValue(styled, '--scope-var', '1px');
     //
     var child = styled.$.child;
     assertStylePropertyValue(child, '--scope-var', '1px');
     assertStylePropertyValue(child, '--child-scope-var', '2px');
     assertStylePropertyValue(child, '--grand-child-scope-var', '3px');
     //
     var gc1 = child.$.gc1;
     assertStylePropertyValue(gc1, '--scope-var', '1px');
     assertStylePropertyValue(gc1, '--child-scope-var', '2px');
     assertStylePropertyValue(gc1, '--grand-child-scope-var', '3px');
     //
     var gc2 = child.$.gc2;
     assertStylePropertyValue(gc2, '--scope-var', '1px');
     assertStylePropertyValue(gc2, '--child-scope-var', '2px');
     assertStylePropertyValue(gc2, '--grand-child-scope-var', '4px');
     //
     var gc3 = child.$.gc3;
     assertStylePropertyValue(gc3, '--scope-var', '1px');
     assertStylePropertyValue(gc3, '--child-scope-var', '2px');
     assertStylePropertyValue(gc3, '--grand-child-scope-var', '3px');

  });

  test('invalid variables not parsed', function() {
    if (Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    assert.notProperty(styled._styleProperties, 'x--invalid');
    assertComputed(styled.$.invalid, '0px');
  });

  test('simple variables applied correctly between scopes', function() {
     assertComputed(styled.$.me, '1px');
     assertComputed(styled.$.child.$.me, '2px');
     assertComputed(styled.$.child.$.gc1.$.me, '3px');
     assertComputed(styled.$.child.$.gc1.$.scope, '1px');
     assertComputed(styled.$.child.$.gc1.$.child, '2px');
     assertComputed(styled.$.child.$.gc2.$.me, '4px');
     assertComputed(styled.$.child.$.gc2.$.scope, '1px');
     assertComputed(styled.$.child.$.gc2.$.child, '2px');
  });

  test('variable can be set to another variable', function() {
    var gc4 = styled.$.child.$.gc4;
    assertStylePropertyValue(gc4, '--grand-child-scope-var', '5px');
    assertComputed(gc4.$.me, '5px');
  });

  test('variable default values can be assigned to other variables', function() {
    assertStylePropertyValue(styled, '--default1', '6px');
    assertStylePropertyValue(styled, '--default2', '7px');
    assertComputed(styled.$.default1, '6px');
    assertComputed(styled.$.default2, '7px');
    assertComputed(styled.$.applyDefault1, '6px');
    assertComputed(styled.$.applyDefault2, '7px');
  });

  test('variable literal defaults can contain one (...)', function() {
    var b = getComputedStyle(styled.$.default3).backgroundColor;
    assert.match(b, /rgb\(128/, 'literal fallback containin (...) not set');
  });

  test('variable values can be used with calc', function() {
    assertComputed(styled.$.calc, '15px');
  });

  test('variable values can be used with box-shadow', function() {
    var b = getComputedStyle(styled.$.shadow).boxShadow;
    assert.match(b, /rgb\(128/, 'box shadow not set correctly');
  });

  test('host properties can be overridden with outer scope styles', function() {
    assertComputed(styled.$.hostProp, '10px');
  });

  test('updateStyles changes property values and using style cache', function() {
    styled.$.child.classList.add('special');
    var l = document.querySelectorAll('style').length;
    styled.updateStyles();
    if (styled.shadyRoot && !Polymer.Settings.useNativeCSSProperties) {
      assert.equal(document.querySelectorAll('style').length, l+4);
    }
    assertComputed(styled.$.child.$.me, '12px');
    styled.$.child.classList.remove('special');
    styled.updateStyles();
    if (styled.shadyRoot && !Polymer.Settings.useNativeCSSProperties) {
      assert.equal(document.querySelectorAll('style').length, l);
    }
    assertComputed(styled.$.child.$.me, '2px');
  });

  test('Nested element uses style cache when attached and updateStyles is called', function() {
    var l = document.querySelectorAll('style').length;
    var e1 = document.createElement('x-produce-use-property');
    var e1t = e1.$.hosted.$.hosted;
    var e2 = document.createElement('x-produce-use-property');
    var e2t = e2.$.hosted.$.hosted;
    document.body.appendChild(e1);
    document.body.appendChild(e2);
    CustomElements.takeRecords();
    if (styled.shadyRoot) {
      assert.equal(document.querySelectorAll('style').length, l+1);
    }
    assertComputed(e1t, '6px');
    assertComputed(e2t, '6px');
    e1.customStyle['--scope-var'] = '8px solid purple';
    e2.customStyle['--scope-var'] = '8px solid purple';
    Polymer.updateStyles();
    assertComputed(e1t, '8px');
    assertComputed(e2t, '8px');
    if (styled.shadyRoot) {
      assert.equal(document.querySelectorAll('style').length, l+1);
    }
    e1.customStyle['--scope-var'] = null;
    e2.customStyle['--scope-var'] = null;
    Polymer.updateStyles();
    assertComputed(e1t, '6px');
    assertComputed(e2t, '6px');
    if (styled.shadyRoot) {
      assert.equal(document.querySelectorAll('style').length, l+1);
    }
  })

  test('updateStyles changes own properties based on customStyle changes', function() {
    styled.$.child.customStyle['--child-scope-var'] = '30px solid orange';
    styled.$.child.updateStyles();
    assertComputed(styled.$.child.$.me, '30px');
    styled.$.child.customStyle = {};
    styled.$.child.updateStyles();
    assertComputed(styled.$.child.$.me, '2px');
  });

  test('updateStyles with properties argument changes styles', function() {
    styled.$.child.updateStyles({'--child-scope-var': '26px solid seagreen'});
    assertComputed(styled.$.child.$.me, '26px');
    styled.$.child.customStyle = {};
    styled.$.child.updateStyles();
    assertComputed(styled.$.child.$.me, '2px');
  });

  test('updateStyles on when element when not attached', function() {
    var x = document.createElement('x-update-styles');
    document.body.appendChild(x);
    CustomElements.takeRecords();
    assertComputed(x, '10px');
    document.body.removeChild(x);
    CustomElements.takeRecords();
    x.updateStyles({'--border': '8px solid orange'});
    document.body.appendChild(x);
    CustomElements.takeRecords();
    assertComputed(x, '8px');
  });

  test('updateStyles called in observer when element not attached', function() {
    var x = document.createElement('x-update-styles');
    document.body.appendChild(x);
    CustomElements.takeRecords();
    assertComputed(x.$.child, '0px');
    x.active = true;
    assertComputed(x.$.child, '6px');
    x.active = false;
    assertComputed(x.$.child, '0px');
    var scopeSelector = x.$.child._scopeSelector;
    document.body.removeChild(x);
    CustomElements.takeRecords();
    x.active = true;
    document.body.appendChild(x);
    CustomElements.takeRecords();
    if (styled.shadyRoot && !Polymer.Settings.useNativeCSSProperties) {
      assert.notEqual(scopeSelector, x.$.child._scopeSelector);
      assert.isTrue(x.$.child.classList.contains(x.$.child._scopeSelector));
      assert.isFalse(x.$.child.classList.contains(scopeSelector));
    }
    assertComputed(x.$.child, '6px');
  });

  test('styles update based on root customStyle changes', function() {
    assertComputed(styled.$.dynamic, '0px');
    Polymer.StyleDefaults.customStyle['--dynamic'] = '4px solid navy';
    Polymer.updateStyles();
    assertComputed(styled.$.dynamic, '4px');
    Polymer.updateStyles({'--dynamic': '6px solid gray'});
    assertComputed(styled.$.dynamic, '6px');
  });

  test('null customStyles unapply', function() {
    styled.$.dynamic.updateStyles({'--dynamic': '8px solid black'});
    assertComputed(styled.$.dynamic, '8px');
    styled.$.dynamic.updateStyles({'--dynamic': null});
    assertComputed(styled.$.dynamic, '6px');
    styled.$.dynamic.updateStyles({'--dynamic': '8px solid black'});
    assertComputed(styled.$.dynamic, '8px');
    styled.$.dynamic.updateStyles({'--dynamic': null});
    assertComputed(styled.$.dynamic, '6px');
  });

  test('style properties with dom-if', function() {
    var e = styled.$.iffy;
    var c = Polymer.dom(e.root).querySelector('.iffy');
    assert.ok(c, 'dom-if did not stamp');
    assertComputed(c, '1px');
  });

  test('variable precedence and overrides', function() {
    // renamed property applied
    var o1a = styled.$.overrides1a;
    assertStylePropertyValue(o1a, '--rename', '8px');
    assertStylePropertyValue(o1a, '--grand-child-scope-var', '8px');
    assertComputed(o1a.$.gc1.$.me, '8px');
    // :host property overridden by outer scope
    var o1b = styled.$.overrides1b;
    assertStylePropertyValue(o1b, '--rename', '8px');
    assertStylePropertyValue(o1b, '--grand-child-scope-var', '9px');
    assertComputed(o1b.$.gc1.$.me, '9px');
    // own scope property overrides outer scope
    var o2 = styled.$.overrides2;
    assertStylePropertyValue(o2, '--rename', '8px');
    assertStylePropertyValue(o2.$.gc1, '--grand-child-scope-var', '8px');
    assertComputed(o2.$.gc1.$.me, '8px');

    // late bound property does *not* resolve using inherited value
    var o3 = styled.$.overrides3;
    if (!Polymer.Settings.useNativeCSSProperties) {
      assert.equal(o3._styleProperties['--late'], '', 'property should not be late bound');
    }
    assertStylePropertyValue(o3.$.late, '--fillin', '16px');
    assertComputed(o3.$.late, '0px');

  });

  test('type extension elements consume properties in :host rules', function() {
    assertComputed(styled.$.button, '16px');
  });

  test('pseudo-elements can consume custom properties', function() {
    assertComputed(styled.$.after, '17px', '::after');
  });

  test('elements using only variable defaults are styled properly', function() {
    assertComputed(styled.$.defaultElement1, '22px');
    assertComputed(styled.$.defaultElement2, '23px');
  });

  test('vars with trailing new line or } apply', function() {
    assertComputed(styled.$.wsTerm, '18px');
    assertComputed(styled.$.endTerm, '19px');
  });

  test('variable with parenthesis', function() {
    var url = getComputedStyle(styled.$.parenthesis).backgroundImage.replace(/['"]/g, '');
    assert.equal('url(http://placehold.it/400x300)', url);
  });

  test('custom style class overrides css variable', function() {
    var d = document.createElement('x-variable-override');
    d.classList.add('variable-override');
    document.body.appendChild(d);
    CustomElements.takeRecords();
    assertComputed(d, '10px');
  });

  test('class selector overrides :host(class) property on element with only dynamic styles', function() {
    var d = document.createElement('x-host-variable-consumer');
    document.body.appendChild(d);
    CustomElements.takeRecords();
    assertComputed(d.$.consumer, '10px');
  });

  test('var values can be overridden by subsequent concrete properties', function() {
    assertComputed(styled.$.overridesConcrete, '4px');
  });

  test('producing a var that consumes another var preserves static styling', function() {
    var d = document.createElement('x-var-produce-via-consume');
    document.body.appendChild(d);
    CustomElements.takeRecords();
    assertComputed(d, '10px');
  });

  test('producing a var that consumes results in static and not dynamic stylesheet', function() {
    if (Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    var d = document.createElement('x-var-produce-via-consume');
    document.body.appendChild(d);
    CustomElements.takeRecords();
    var styleRoot = d.shadowRoot ? d.shadowRoot : document.head;
    var staticStyle = styleRoot.querySelector('style[scope=x-var-produce-via-consume]');
    assert.ok(staticStyle);
    assert.match(staticStyle.textContent, /display/, 'static style does not contain style content');
    assert.equal(styleRoot.querySelectorAll('style[scope~=x-var-produce-via-consume]').length, 1);
  });

  test('Nested var fallback syntax', function() {
    assertComputed(styled.$.nestedFallback, '20px');
    assertComputed(styled.$.doubleNestedFallback, '20px');
    var url = getComputedStyle(styled.$.cornerFallback).backgroundImage.replace(/['"]/g, '');
    assert.equal('url(http://placehold.it/400x300)', url);
  });

  test('bad fallback syntax `var(--a, --b)` is corrected', function() {
    assertComputed(styled.$.badFallback, '6px');
    assertComputed(styled.$.funkyFallback, 'rgb(128, 128, 128)', null, 'color');
  });

  test('invalid @media rules do not match', function() {
    var e = document.createElement('x-non-media-matching');
    document.body.appendChild(e);
    assertComputed(e.$.inside, '0px');
  });

  test('invalid @media rules do not match :host rule', function() {
    var e = document.createElement('x-non-media-matching-host');
    document.body.appendChild(e);
    assertComputed(e, '0px');
  });

  test('complex fallback for variables work as expected', function() {
    var e = document.createElement('x-super-complex');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assertComputed(e, 'rgb(102, 205, 170)', null, 'color');
  });

  test('updateStyles plays nicely with inline styling', function() {
    if (!Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    var e = document.createElement('x-dynamic');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    e.style.cssText = 'background-color: rgb(255, 0, 0);';
    e.updateStyles();
    assertComputed(e, 'rgb(255, 0, 0)', null, 'background-color');
  });

  test('updateStyles removes the correct number of style properties', function() {
    if (!Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    var e = document.createElement('x-dynamic');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    e.style.cssText = 'background-color: rgb(255, 0, 0);';
    e.updateStyles({'--dynamic': '2px solid black'});
    assertComputed(e, 'rgb(255, 0, 0)', null, 'background-color');
    assertComputed(e, '2px');
    e.updateStyles({'--dynamic': null});
    assertComputed(e, 'rgb(255, 0, 0)', null, 'background-color');
    assertComputed(e, '6px');
  });

  test('"initial" as a custom property value works as expected', function() {
    if (Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    var e = document.createElement('x-initial');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assertComputed(e, '10px');
  });

  test('"inherit" as a custom property value works as expected', function() {
    if (Polymer.Settings.useNativeCSSProperties) {
      this.skip();
    }
    var e = document.createElement('x-inherit');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assertComputed(e, '10px');

    e = document.createElement('x-inherit-parent');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assertComputed(e.$.child, '5px');
  });

  test(':root and :host > * rules apply to all shadowroot children', function() {
    var e = document.createElement('root-styles');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assertComputed(e.$.root, '2px');
    assertComputed(e.$.host, '10px');
  });

  test('elements in the dom should be able to query for inherited variables', function() {
    var e = document.createElement('prop-outer');
    document.body.appendChild(e);
    CustomElements.takeRecords();
    assert.equal(e.getComputedStyleValue('--foo').trim(), 'orange');
    assert.equal(e.$.inner.getComputedStyleValue('--foo').trim(), 'orange');
  })
});

</script>
</body>
</html>
